//! Database configuration and initialization

use crate::error::Result;
use crate::storage::{DatabaseStorage, RedisCache, TestCache};
use serde::{Deserialize, Serialize};
use std::env;
use std::time::Duration;

/// Database configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseConfig {
    /// PostgreSQL database URL
    pub database_url: String,
    /// Maximum number of connections in the pool
    pub max_connections: u32,
    /// Connection timeout in seconds
    pub connection_timeout: u64,
    /// Idle timeout in seconds
    pub idle_timeout: u64,
    /// Enable SQL query logging
    pub enable_logging: bool,
}

impl Default for DatabaseConfig {
    fn default() -> Self {
        Self {
            database_url: "postgresql://postgres:password@localhost/ai_test_platform".to_string(),
            max_connections: 10,
            connection_timeout: 30,
            idle_timeout: 600,
            enable_logging: false,
        }
    }
}

impl DatabaseConfig {
    /// Create configuration from environment variables
    pub fn from_env() -> Self {
        Self {
            database_url: env::var("DATABASE_URL")
                .unwrap_or_else(|_| Self::default().database_url),
            max_connections: env::var("DATABASE_MAX_CONNECTIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().max_connections),
            connection_timeout: env::var("DATABASE_CONNECTION_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().connection_timeout),
            idle_timeout: env::var("DATABASE_IDLE_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().idle_timeout),
            enable_logging: env::var("DATABASE_ENABLE_LOGGING")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().enable_logging),
        }
    }
}

/// Redis cache configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
    /// Redis connection URL
    pub redis_url: String,
    /// Default TTL for cache entries in seconds
    pub default_ttl: u64,
    /// Maximum number of connections in the pool
    pub max_connections: u32,
    /// Connection timeout in seconds
    pub connection_timeout: u64,
    /// Enable cache compression
    pub enable_compression: bool,
}

impl Default for CacheConfig {
    fn default() -> Self {
        Self {
            redis_url: "redis://localhost:6379".to_string(),
            default_ttl: 3600, // 1 hour
            max_connections: 10,
            connection_timeout: 5,
            enable_compression: false,
        }
    }
}

impl CacheConfig {
    /// Create configuration from environment variables
    pub fn from_env() -> Self {
        Self {
            redis_url: env::var("REDIS_URL")
                .unwrap_or_else(|_| Self::default().redis_url),
            default_ttl: env::var("CACHE_DEFAULT_TTL")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().default_ttl),
            max_connections: env::var("CACHE_MAX_CONNECTIONS")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().max_connections),
            connection_timeout: env::var("CACHE_CONNECTION_TIMEOUT")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().connection_timeout),
            enable_compression: env::var("CACHE_ENABLE_COMPRESSION")
                .ok()
                .and_then(|s| s.parse().ok())
                .unwrap_or(Self::default().enable_compression),
        }
    }
}

/// Storage layer configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageConfig {
    pub database: DatabaseConfig,
    pub cache: CacheConfig,
}

impl Default for StorageConfig {
    fn default() -> Self {
        Self {
            database: DatabaseConfig::default(),
            cache: CacheConfig::default(),
        }
    }
}

impl StorageConfig {
    /// Create configuration from environment variables
    pub fn from_env() -> Self {
        Self {
            database: DatabaseConfig::from_env(),
            cache: CacheConfig::from_env(),
        }
    }
}

/// Storage layer manager
#[derive(Debug, Clone)]
pub struct StorageManager {
    pub database: DatabaseStorage,
    pub cache: RedisCache,
}

impl StorageManager {
    /// Initialize storage layer with configuration
    pub async fn new(config: StorageConfig) -> Result<Self> {
        // Initialize database
        let database = DatabaseStorage::new(&config.database.database_url).await?;

        // Initialize cache
        let cache = RedisCache::new(&config.cache.redis_url).await?;

        Ok(Self { database, cache })
    }

    /// Initialize storage layer from environment variables
    pub async fn from_env() -> Result<Self> {
        let config = StorageConfig::from_env();
        Self::new(config).await
    }

    /// Health check for storage components
    pub async fn health_check(&self) -> Result<StorageHealth> {
        let database_healthy = self.check_database_health().await;
        let cache_healthy = self.check_cache_health().await;

        Ok(StorageHealth {
            database: database_healthy,
            cache: cache_healthy,
            overall: database_healthy && cache_healthy,
        })
    }

    /// Check database health
    async fn check_database_health(&self) -> bool {
        // Try a simple query to check database connectivity
        sqlx::query("SELECT 1")
            .fetch_optional(self.database.pool())
            .await
            .is_ok()
    }

    /// Check cache health
    async fn check_cache_health(&self) -> bool {
        // Try a simple ping to check cache connectivity
        self.cache.exists("__health_check__").await.is_ok()
    }

    /// Close all storage connections
    pub async fn close(&self) {
        self.database.close().await;
        // Redis connections are automatically managed
    }
}

/// Storage health status
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageHealth {
    pub database: bool,
    pub cache: bool,
    pub overall: bool,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_database_config_default() {
        let config = DatabaseConfig::default();
        assert!(config.database_url.contains("postgresql"));
        assert_eq!(config.max_connections, 10);
    }

    #[test]
    fn test_cache_config_default() {
        let config = CacheConfig::default();
        assert!(config.redis_url.contains("redis"));
        assert_eq!(config.default_ttl, 3600);
    }

    #[test]
    fn test_storage_config_from_env() {
        // This test would require setting environment variables
        // For now, just test that it doesn't panic
        let _config = StorageConfig::from_env();
    }
}
